﻿using System.Reflection;
using NLite.Internal;
using System;

namespace NLite.Reflection
{
	delegate void Setter(object target, object value);

	/// <summary>
	/// 
	/// </summary>
	public static class SetterExtensions
	{
        /// <summary>
        /// 
        /// </summary>
        /// <param name="member"></param>
        /// <returns></returns>
        public static Action<TTarget, TValue> GetSetter<TTarget, TValue>(this FieldInfo member)
        {
            Setter setter = null;
            if (member.DeclaringType.IsValueType)
                setter = (target,value) => member.SetValue(target,value);
            else
                setter = DynamicMethodFactoryProxy.Instance.CreateSetter(member);

            return (target, value) => setter(target,value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="member"></param>
        /// <returns></returns>
        public static Action<TTarget, TValue> GetSetter<TTarget, TValue>(this PropertyInfo member)
        {
            Setter setter = null;
            if (member.DeclaringType.IsValueType)
                setter = (target, value) => member.SetValue(target, value,null);
            else
                setter = DynamicMethodFactoryProxy.Instance.CreateSetter(member);

            return (target, value) => setter(target, value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="member"></param>
        /// <returns></returns>
        public static Action<TTarget, TValue> GetSetter<TTarget, TValue>(this MethodInfo member)
        {
            Setter setter = null;
            if (member.DeclaringType.IsValueType)
                setter = (target, value) => member.Invoke(target, new object[] { value });
            else
                setter = DynamicMethodFactoryProxy.Instance.CreateSetter(member);

            return (target, value) => setter(target, value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="member"></param>
        /// <returns></returns>
        public static Action<TTarget, TValue> GetSetter<TTarget, TValue>(this MemberInfo member)
        {
            switch (member.MemberType)
            {
                case MemberTypes.Field: return GetSetter<TTarget, TValue>(member as FieldInfo);
                case MemberTypes.Property: return GetSetter<TTarget, TValue>(member as PropertyInfo);
                case MemberTypes.Method: return GetSetter<TTarget, TValue>(member as MethodInfo);
            }

            return null;
        }
		/// <summary>
		/// 
		/// </summary>
		/// <param name="member"></param>
		/// <param name="target"></param>
		/// <param name="value"></param>
		public static void Set(this MemberInfo member, object target, object value)
		{

			if (target != null && target.GetType().IsValueType)
			{
				switch (member.MemberType)
				{
						case MemberTypes.Field: (member as FieldInfo).SetValue(target, value); break;
						case MemberTypes.Property: (member as PropertyInfo).SetValue(target, value, null); break;
						case MemberTypes.Method: (member as MethodInfo).Invoke(target, new object[] { value }); break;
				}
			}
			else
			{
				try
				{
					DynamicMethodFactoryProxy.Instance.CreateSetter(member)(target, value);
				}
				catch(TargetInvocationException ex)
				{
					throw ex.InnerException;
				}
			}
		}
	}
}
